#define __host__
#define __device__

#include <vector_types.h>

#include <algorithm/analytical_model/algorithm/cuda/inter_op/receiver/RectangleReceiver.h>
#include <algorithm/analytical_model/algorithm/cuda/receiver/RectangleReceiver.cuh>
#include <utils/Utils.h>

#include <memory>

using namespace solar::cuda::inter_op;

RectangleReceiver::RectangleReceiver(cudaStream_t stream)
{
    this->delegate_ptr_ = std::make_shared<solar::cuda::RectangleReceiver>(stream);
}

RectangleReceiver::RectangleReceiver(const RectangleReceiver& rectangle_receiver)
    : Receiver(rectangle_receiver)
{
    auto derived_ptr =
        std::dynamic_pointer_cast<solar::cuda::RectangleReceiver>(rectangle_receiver.delegate_ptr_);
    this->delegate_ptr_ = std::make_shared<solar::cuda::RectangleReceiver>(*derived_ptr);
}

auto RectangleReceiver::intersect(const Float3& orig,
                                  const Float3& dir) const -> std::tuple<float, float, float, bool>
{
    auto derived_ptr =
        std::dynamic_pointer_cast<solar::cuda::RectangleReceiver>(this->delegate_ptr_);
    return derived_ptr->intersect(structCopy<float3>(orig), structCopy<float3>(dir));
}

void RectangleReceiver::init(float pixel_per_meter_for_receiver) const
{
    auto derived_ptr =
        std::dynamic_pointer_cast<solar::cuda::RectangleReceiver>(this->delegate_ptr_);
    derived_ptr->init(pixel_per_meter_for_receiver);
}

void RectangleReceiver::setResolution(float pixel_per_meter_for_receiver) const
{
    auto derived_ptr =
        std::dynamic_pointer_cast<solar::cuda::RectangleReceiver>(this->delegate_ptr_);
    derived_ptr->setResolution(pixel_per_meter_for_receiver);
}

auto RectangleReceiver::getFocusCenter(const Float3& heliostat_position) const -> Float3
{
    auto derived_ptr =
        std::dynamic_pointer_cast<solar::cuda::RectangleReceiver>(this->delegate_ptr_);
    auto result = derived_ptr->getFocusCenter(structCopy<float3>(heliostat_position));
    return structCopy<Float3>(result);
}

void RectangleReceiver::setFocusCenter() const
{
    auto derived_ptr =
        std::dynamic_pointer_cast<solar::cuda::RectangleReceiver>(this->delegate_ptr_);
    derived_ptr->setFocusCenter();
}

auto RectangleReceiver::getVertices() const -> std::array<Float3, 4>
{
    auto derived_ptr =
        std::dynamic_pointer_cast<solar::cuda::RectangleReceiver>(this->delegate_ptr_);
    return {structCopy<Float3>(derived_ptr->rect_vertices_[0]),
            structCopy<Float3>(derived_ptr->rect_vertices_[1]),
            structCopy<Float3>(derived_ptr->rect_vertices_[2]),
            structCopy<Float3>(derived_ptr->rect_vertices_[3])};
}

void RectangleReceiver::setVertices(const std::array<Float3, 4>& vertices)
{
    auto derived_ptr =
        std::dynamic_pointer_cast<solar::cuda::RectangleReceiver>(this->delegate_ptr_);
    std::transform(vertices.begin(), vertices.end(), derived_ptr->rect_vertices_,
                   [&](const Float3& current_vertex)
                   { return structCopy<float3>(current_vertex); });
}
